import sys
import os
import cv2
import numpy as np
from PyQt5.QtWidgets import (QApplication, QMainWindow, QTabWidget, QLabel, QPushButton,
                             QVBoxLayout, QHBoxLayout, QFileDialog, QSlider, QSpinBox,
                             QComboBox, QGroupBox, QGridLayout, QMessageBox, QSplitter,
                             QListWidget, QLineEdit, QAction, QToolBar, QStatusBar,
                             QDockWidget, QDialog, QFormLayout, QProgressBar, QCheckBox)
from PyQt5.QtGui import QPixmap, QImage, QIcon, QPainter, QPen, QColor
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer, QPoint
from PyQt5.QtWidgets import QWidget
import sqlite3
import time

# 设置中文字体支持
os.environ["QT_FONT_DPI"] = "96"


class ImageProcessor:
    """图像处理核心类，封装各种图像处理算法"""

    def __init__(self):
        self.image = None
        self.original_image = None
        self.current_effect = None

    def load_image(self, file_path):
        """加载图像"""
        try:
            self.original_image = cv2.imread(file_path)
            if self.original_image is None:
                return False
            self.image = self.original_image.copy()
            return True
        except Exception as e:
            print(f"加载图像失败: {e}")
            return False

    def rotate_image(self, angle):
        """旋转图像"""
        if self.image is None:
            return False

        (h, w) = self.image.shape[:2]
        center = (w // 2, h // 2)
        M = cv2.getRotationMatrix2D(center, angle, 1.0)
        self.image = cv2.warpAffine(self.image, M, (w, h))
        return True

    def resize_image(self, scale_percent):
        """缩放图像"""
        if self.image is None:
            return False

        width = int(self.image.shape[1] * scale_percent / 100)
        height = int(self.image.shape[0] * scale_percent / 100)
        dim = (width, height)
        self.image = cv2.resize(self.image, dim, interpolation=cv2.INTER_AREA)
        return True

    def crop_image(self, x1, y1, x2, y2):
        """裁剪图像"""
        if self.image is None:
            return False

        self.image = self.image[y1:y2, x1:x2]
        return True

    def adjust_brightness_contrast(self, brightness=0, contrast=1.0):
        """调整亮度和对比度"""
        if self.image is None:
            return False

        # 确保对比度在合理范围内
        contrast = max(0.1, min(3.0, contrast))

        # 调整亮度和对比度
        self.image = cv2.convertScaleAbs(self.image, alpha=contrast, beta=brightness)
        return True

    def adjust_saturation(self, saturation=1.0):
        """调整饱和度"""
        if self.image is None:
            return False

        # 转换为HSV色彩空间
        hsv = cv2.cvtColor(self.image, cv2.COLOR_BGR2HSV)

        # 调整饱和度
        hsv[:, :, 1] = np.clip(hsv[:, :, 1] * saturation, 0, 255).astype(hsv.dtype)

        # 转回BGR色彩空间
        self.image = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
        return True

    def apply_smoothing(self, method, kernel_size=3):
        """应用平滑处理"""
        if self.image is None:
            return False

        if method == "均值滤波":
            self.image = cv2.blur(self.image, (kernel_size, kernel_size))
        elif method == "高斯滤波":
            self.image = cv2.GaussianBlur(self.image, (kernel_size, kernel_size), 0)
        elif method == "中值滤波":
            self.image = cv2.medianBlur(self.image, kernel_size)
        return True

    def detect_shapes(self):
        """检测图像中的形状"""
        if self.image is None:
            return []

        # 转换为灰度图
        gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)

        # 二值化处理
        _, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

        # 查找轮廓
        contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        shapes = []
        output = self.image.copy()

        for contour in contours:
            # 计算轮廓面积
            area = cv2.contourArea(contour)

            # 忽略小面积噪声
            if area < 100:
                continue

            # 近似轮廓
            epsilon = 0.04 * cv2.arcLength(contour, True)
            approx = cv2.approxPolyDP(contour, epsilon, True)

            # 根据顶点数量判断形状
            vertices = len(approx)
            shape = "未知"

            if vertices == 3:
                shape = "三角形"
            elif vertices == 4:
                shape = "矩形"
            elif vertices > 4:
                # 计算轮廓的圆形度
                perimeter = cv2.arcLength(contour, True)
                circularity = 4 * np.pi * area / (perimeter * perimeter)

                if circularity > 0.8:
                    shape = "圆形"
                else:
                    shape = "多边形"

            # 获取边界框
            x, y, w, h = cv2.boundingRect(approx)

            # 存储形状信息
            shapes.append({
                "type": shape,
                "vertices": vertices,
                "area": area,
                "contour": contour,
                "bbox": (x, y, w, h)
            })

            # 在图像上绘制轮廓和标签
            cv2.drawContours(output, [contour], -1, (0, 255, 0), 2)
            cv2.putText(output, shape, (x, y - 10),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)

        self.image = output
        return shapes

    def stitch_images(self, images):
        """图像拼接"""
        if not images:
            return None

        stitcher = cv2.Stitcher_create()
        status, result = stitcher.stitch(images)

        if status == cv2.Stitcher_OK:
            self.image = result
            return True
        else:
            print(f"图像拼接失败，状态码: {status}")
            return False

    def detect_faces(self):
        """检测人脸"""
        if self.image is None:
            return []

        # 转换为RGB格式（face_recognition库使用RGB）
        rgb_image = cv2.cvtColor(self.image, cv2.COLOR_BGR2RGB)

        # 检测人脸位置
        face_locations = face_recognition.face_locations(rgb_image)

        # 检测人脸特征点
        face_landmarks = face_recognition.face_landmarks(rgb_image, face_locations)

        # 生成人脸编码（用于识别）
        face_encodings = face_recognition.face_encodings(rgb_image, face_locations)

        faces = []
        output = self.image.copy()

        for (top, right, bottom, left), encoding, landmarks in zip(
                face_locations, face_encodings, face_landmarks):

            # 存储人脸信息
            face_info = {
                "location": (top, right, bottom, left),
                "encoding": encoding,
                "landmarks": landmarks
            }

            faces.append(face_info)

            # 在图像上绘制人脸框
            cv2.rectangle(output, (left, top), (right, bottom), (0, 0, 255), 2)

            # 绘制面部特征点
            for feature in landmarks:
                for point in landmarks[feature]:
                    cv2.circle(output, point, 2, (0, 255, 0), -1)

        self.image = output
        return faces

    def recognize_faces(self, face_encodings, tolerance=0.6):
        """人脸识别"""
        if not face_encodings:
            return []

        # 从数据库加载已知人脸
        known_faces = self._load_known_faces()

        results = []

        for encoding in face_encodings:
            matches = []

            for known_face in known_faces:
                # 比较人脸编码
                match = face_recognition.compare_faces(
                    [known_face["encoding"]], encoding, tolerance=tolerance)

                if match[0]:
                    # 计算相似度
                    distance = face_recognition.face_distance(
                        [known_face["encoding"]], encoding)

                    similarity = max(0, min(100, int((1 - distance[0]) * 100)))

                    matches.append({
                        "name": known_face["name"],
                        "similarity": similarity
                    })

            # 按相似度排序
            if matches:
                matches.sort(key=lambda x: x["similarity"], reverse=True)
                results.append(matches[0])
            else:
                results.append({"name": "未知", "similarity": 0})

        return results

    def _load_known_faces(self):
        """从数据库加载已知人脸"""
        faces = []

        try:
            conn = sqlite3.connect("faces.db")
            cursor = conn.cursor()

            # 创建人脸表（如果不存在）
            cursor.execute('''CREATE TABLE IF NOT EXISTS faces
                             (id INTEGER PRIMARY KEY AUTOINCREMENT,
                             name TEXT NOT NULL,
                             encoding TEXT NOT NULL)''')

            # 查询所有人脸
            cursor.execute("SELECT name, encoding FROM faces")
            rows = cursor.fetchall()

            for row in rows:
                name, encoding_str = row
                # 将存储的编码字符串转回numpy数组
                encoding = np.fromstring(encoding_str, sep=',')
                faces.append({"name": name, "encoding": encoding})

            conn.close()
        except Exception as e:
            print(f"加载人脸数据库失败: {e}")

        return faces

    def save_face_encoding(self, name, encoding):
        """保存人脸编码到数据库"""
        try:
            conn = sqlite3.connect("faces.db")
            cursor = conn.cursor()

            # 创建人脸表（如果不存在）
            cursor.execute('''CREATE TABLE IF NOT EXISTS faces
                             (id INTEGER PRIMARY KEY AUTOINCREMENT,
                             name TEXT NOT NULL,
                             encoding TEXT NOT NULL)''')

            # 将编码转换为字符串存储
            encoding_str = ','.join(map(str, encoding))

            # 插入新记录
            cursor.execute("INSERT INTO faces (name, encoding) VALUES (?, ?)",
                           (name, encoding_str))

            conn.commit()
            conn.close()
            return True
        except Exception as e:
            print(f"保存人脸编码失败: {e}")
            return False


class VideoProcessor(QThread):
    """视频处理线程"""

    frame_updated = pyqtSignal(np.ndarray)
    detection_updated = pyqtSignal(list)
    processing_finished = pyqtSignal()

    def __init__(self, source=0, processor=None):
        super().__init__()
        self.source = source
        self.processor = processor
        self.cap = None
        self.running = False
        self.apply_effect = False
        self.effect_type = None
        self.detect_faces = False

    def run(self):
        """线程运行函数"""
        self.running = True

        # 打开视频源
        if isinstance(self.source, int):
            # 摄像头
            self.cap = cv2.VideoCapture(self.source)
        else:
            # 视频文件
            self.cap = cv2.VideoCapture(self.source)

        if not self.cap.isOpened():
            self.processing_finished.emit()
            return

        while self.running:
            ret, frame = self.cap.read()

            if not ret:
                break

            # 处理帧
            processed_frame = frame.copy()

            if self.apply_effect:
                if self.effect_type == "灰度":
                    processed_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
                    processed_frame = cv2.cvtColor(processed_frame, cv2.COLOR_GRAY2BGR)
                elif self.effect_type == "边缘检测":
                    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
                    processed_frame = cv2.Canny(gray, 100, 200)
                    processed_frame = cv2.cvtColor(processed_frame, cv2.COLOR_GRAY2BGR)

            # 人脸检测
            face_locations = []
            if self.detect_faces and self.processor:
                # 转换为RGB格式
                rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

                # 检测人脸位置
                face_locations = face_recognition.face_locations(rgb_frame)

                # 在帧上绘制人脸框
                for (top, right, bottom, left) in face_locations:
                    cv2.rectangle(processed_frame, (left, top), (right, bottom),
                                  (0, 0, 255), 2)

            # 发送更新信号
            self.frame_updated.emit(processed_frame)
            self.detection_updated.emit(face_locations)

            # 控制帧率
            self.msleep(30)

        self.cap.release()
        self.processing_finished.emit()

    def stop(self):
        """停止线程"""
        self.running = False
        self.wait()


class FaceRegistrationDialog(QDialog):
    """人脸注册对话框"""

    face_registered = pyqtSignal(str, np.ndarray)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("人脸注册")
        self.resize(400, 300)

        layout = QVBoxLayout(self)

        # 姓名输入
        form_layout = QFormLayout()
        self.name_input = QLineEdit()
        form_layout.addRow("姓名:", self.name_input)

        # 人脸图像预览
        self.preview_label = QLabel("人脸预览")
        self.preview_label.setAlignment(Qt.AlignCenter)
        self.preview_label.setMinimumSize(300, 200)
        self.preview_label.setStyleSheet("border: 1px solid #ccc;")

        # 按钮
        button_layout = QHBoxLayout()
        self.select_image_btn = QPushButton("选择图像")
        self.select_image_btn.clicked.connect(self.select_image)

        self.register_btn = QPushButton("注册")
        self.register_btn.clicked.connect(self.register_face)
        self.register_btn.setEnabled(False)

        button_layout.addWidget(self.select_image_btn)
        button_layout.addWidget(self.register_btn)

        layout.addLayout(form_layout)
        layout.addWidget(self.preview_label)
        layout.addLayout(button_layout)

        self.selected_image = None

    def select_image(self):
        """选择图像文件"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择人脸图像", "", "图像文件 (*.png *.jpg *.jpeg)")

        if file_path:
            self.selected_image = cv2.imread(file_path)

            # 显示预览
            rgb_image = cv2.cvtColor(self.selected_image, cv2.COLOR_BGR2RGB)
            h, w, ch = rgb_image.shape
            bytes_per_line = ch * w
            q_img = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888)
            self.preview_label.setPixmap(QPixmap.fromImage(q_img).scaled(
                self.preview_label.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))

            self.register_btn.setEnabled(True)

    def register_face(self):
        """注册人脸"""
        if self.selected_image is None:
            QMessageBox.warning(self, "警告", "请先选择人脸图像")
            return

        name = self.name_input.text().strip()
        if not name:
            QMessageBox.warning(self, "警告", "请输入姓名")
            return

        # 检测人脸
        rgb_image = cv2.cvtColor(self.selected_image, cv2.COLOR_BGR2RGB)
        face_encodings = face_recognition.face_encodings(rgb_image)

        if not face_encodings:
            QMessageBox.warning(self, "警告", "未检测到人脸，请选择包含清晰人脸的图像")
            return

        # 获取第一个检测到的人脸编码
        face_encoding = face_encodings[0]

        # 发送注册信号
        self.face_registered.emit(name, face_encoding)

        # 关闭对话框
        self.accept()


class CVCanvas(QLabel):
    """自定义图像显示画布"""

    mouse_pressed = pyqtSignal(QPoint)
    mouse_released = pyqtSignal(QPoint)
    mouse_moved = pyqtSignal(QPoint)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setAlignment(Qt.AlignCenter)
        self.setStyleSheet("background-color: #f0f0f0;")
        self.pixmap = None
        self.scaled_pixmap = None
        self.drawing = False
        self.start_point = QPoint()
        self.end_point = QPoint()
        self.show_crop_rect = False

    def set_image(self, image):
        """设置显示的图像"""
        if isinstance(image, np.ndarray):
            # 转换OpenCV图像到QPixmap
            if len(image.shape) == 3:  # BGR格式
                h, w, ch = image.shape
                bytes_per_line = ch * w
                q_img = QImage(image.data, w, h, bytes_per_line, QImage.Format_BGR888)
            else:  # 灰度图
                h, w = image.shape
                q_img = QImage(image.data, w, h, w, QImage.Format_Grayscale8)

            self.pixmap = QPixmap.fromImage(q_img)
        else:
            self.pixmap = image

        self._update_scaled_pixmap()
        self.update()

    def resizeEvent(self, event):
        """窗口大小改变时重新缩放图像"""
        if self.pixmap:
            self._update_scaled_pixmap()
            self.update()
        super().resizeEvent(event)

    def paintEvent(self, event):
        """绘制事件"""
        super().paintEvent(event)

        if not self.scaled_pixmap:
            return

        painter = QPainter(self)
        painter.drawPixmap(self.rect().center() - self.scaled_pixmap.rect().center(),
                           self.scaled_pixmap)

        # 绘制裁剪矩形
        if self.show_crop_rect and self.drawing:
            pen = QPen(QColor(255, 0, 0))
            pen.setWidth(2)
            painter.setPen(pen)

            # 计算实际矩形位置（考虑图像居中显示）
            offset = self.rect().center() - self.scaled_pixmap.rect().center()
            rect = QRect(self.start_point + offset, self.end_point + offset)
            painter.drawRect(rect)

            # 绘制矩形信息
            font = painter.font()
            font.setPointSize(10)
            painter.setFont(font)

            w = abs(self.end_point.x() - self.start_point.x())
            h = abs(self.end_point.y() - self.start_point.y())

            painter.drawText(rect.topLeft() + QPoint(5, -5), f"{w}x{h}")

    def mousePressEvent(self, event):
        """鼠标按下事件"""
        if event.button() == Qt.LeftButton and self.pixmap:
            # 计算相对于图像的坐标
            offset = self.rect().center() - self.scaled_pixmap.rect().center()
            pos = event.pos() - offset

            # 检查是否在图像区域内
            if self.scaled_pixmap.rect().contains(pos):
                self.drawing = True
                self.start_point = pos
                self.end_point = pos
                self.mouse_pressed.emit(pos)
                self.update()

    def mouseMoveEvent(self, event):
        """鼠标移动事件"""
        if self.drawing and self.pixmap:
            # 计算相对于图像的坐标
            offset = self.rect().center() - self.scaled_pixmap.rect().center()
            pos = event.pos() - offset

            # 检查是否在图像区域内
            if self.scaled_pixmap.rect().contains(pos):
                self.end_point = pos
                self.mouse_moved.emit(pos)
                self.update()

    def mouseReleaseEvent(self, event):
        """鼠标释放事件"""
        if event.button() == Qt.LeftButton and self.drawing and self.pixmap:
            self.drawing = False

            # 计算相对于图像的坐标
            offset = self.rect().center() - self.scaled_pixmap.rect().center()
            pos = event.pos() - offset

            # 检查是否在图像区域内
            if self.scaled_pixmap.rect().contains(pos):
                self.end_point = pos
                self.mouse_released.emit(pos)
                self.update()

    def get_crop_rect(self):
        """获取裁剪矩形（归一化坐标）"""
        if not self.pixmap or not self.show_crop_rect:
            return None

        # 确保起点和终点正确排序
        x1 = min(self.start_point.x(), self.end_point.x())
        y1 = min(self.start_point.y(), self.end_point.y())
        x2 = max(self.start_point.x(), self.end_point.x())
        y2 = max(self.start_point.y(), self.end_point.y())

        # 归一化到原始图像尺寸
        scale_x = self.pixmap.width() / self.scaled_pixmap.width()
        scale_y = self.pixmap.height() / self.scaled_pixmap.height()

        return (int(x1 * scale_x), int(y1 * scale_y),
                int(x2 * scale_x), int(y2 * scale_y))

    def _update_scaled_pixmap(self):
        """更新缩放后的图像"""
        if self.pixmap:
            self.scaled_pixmap = self.pixmap.scaled(
                self.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation)


class MainWindow(QMainWindow):
    """主窗口类"""

    def __init__(self):
        super().__init__()
        self.setWindowTitle("计算机视觉项目实施平台")
        self.resize(1200, 800)

        # 初始化图像处理器
        self.processor = ImageProcessor()

        # 初始化视频处理器
        self.video_processor = None

        # 创建UI
        self._create_menu_bar()
        self._create_tool_bar()
        self._create_main_layout()
        self._create_dock_widgets()
        self._create_status_bar()

        # 连接信号和槽
        self._connect_signals()

        # 当前模式（图像/视频）
        self.mode = "image"

        # 初始化人脸注册对话框
        self.face_registration_dialog = FaceRegistrationDialog(self)
        self.face_registration_dialog.face_registered.connect(self.register_face)

    def _create_menu_bar(self):
        """创建菜单栏"""
        menu_bar = self.menuBar()

        # 文件菜单
        file_menu = menu_bar.addMenu("文件")

        # 加载图像动作
        load_image_action = QAction("加载图像", self)
        load_image_action.setShortcut("Ctrl+I")
        load_image_action.triggered.connect(self.load_image)
        file_menu.addAction(load_image_action)

        # 加载视频动作
        load_video_action = QAction("加载视频", self)
        load_video_action.setShortcut("Ctrl+V")
        load_video_action.triggered.connect(self.load_video)
        file_menu.addAction(load_video_action)

        # 使用摄像头动作
        use_camera_action = QAction("使用摄像头", self)
        use_camera_action.setShortcut("Ctrl+C")
        use_camera_action.triggered.connect(self.use_camera)
        file_menu.addAction(use_camera_action)

        file_menu.addSeparator()

        # 保存动作
        save_action = QAction("保存结果", self)
        save_action.setShortcut("Ctrl+S")
        save_action.triggered.connect(self.save_result)
        file_menu.addAction(save_action)

        file_menu.addSeparator()

        # 退出动作
        exit_action = QAction("退出", self)
        exit_action.setShortcut("Ctrl+Q")
        exit_action.triggered.connect(self.close)
        file_menu.addAction(exit_action)

        # 编辑菜单
        edit_menu = menu_bar.addMenu("编辑")

        # 撤销动作
        undo_action = QAction("撤销", self)
        undo_action.setShortcut("Ctrl+Z")
        undo_action.triggered.connect(self.undo)
        edit_menu.addAction(undo_action)

        # 重做动作
        redo_action = QAction("重做", self)
        redo_action.setShortcut("Ctrl+Y")
        edit_menu.addAction(redo_action)

        # 编辑菜单分隔符
        edit_menu.addSeparator()

        # 旋转动作
        rotate_menu = edit_menu.addMenu("旋转")

        rotate_90_action = QAction("90°", self)
        rotate_90_action.triggered.connect(lambda: self.rotate_image(90))
        rotate_menu.addAction(rotate_90_action)

        rotate_180_action = QAction("180°", self)
        rotate_180_action.triggered.connect(lambda: self.rotate_image(180))
        rotate_menu.addAction(rotate_180_action)

        rotate_270_action = QAction("270°", self)
        rotate_270_action.triggered.connect(lambda: self.rotate_image(270))
        rotate_menu.addAction(rotate_270_action)

        rotate_free_action = QAction("自定义角度...", self)
        rotate_free_action.triggered.connect(self.rotate_image_free)
        rotate_menu.addAction(rotate_free_action)

        # 缩放动作
        scale_menu = edit_menu.addMenu("缩放")

        scale_50_action = QAction("50%", self)
        scale_50_action.triggered.connect(lambda: self.resize_image(50))
        scale_menu.addAction(scale_50_action)

        scale_100_action = QAction("100%", self)
        scale_100_action.triggered.connect(lambda: self.resize_image(100))
        scale_menu.addAction(scale_100_action)

        scale_200_action = QAction("200%", self)
        scale_200_action.triggered.connect(lambda: self.resize_image(200))
        scale_menu.addAction(scale_200_action)

        scale_free_action = QAction("自定义比例...", self)
        scale_free_action.triggered.connect(self.resize_image_free)
        scale_menu.addAction(scale_free_action)

        # 裁剪动作
        crop_action = QAction("裁剪", self)
        crop_action.setShortcut("Ctrl+X")
        crop_action.triggered.connect(self.toggle_crop_mode)
        edit_menu.addAction(crop_action)

        # 算法菜单
        algorithm_menu = menu_bar.addMenu("算法")

        # 形状识别动作
        shape_detection_action = QAction("形状识别", self)
        shape_detection_action.triggered.connect(self.detect_shapes)
        algorithm_menu.addAction(shape_detection_action)

        # 人脸检测动作
        face_detection_action = QAction("人脸检测", self)
        face_detection_action.triggered.connect(self.detect_faces)
        algorithm_menu.addAction(face_detection_action)

        # 人脸识别动作
        face_recognition_action = QAction("人脸识别", self)
        face_recognition_action.triggered.connect(self.recognize_faces)
        algorithm_menu.addAction(face_recognition_action)

        # 人脸注册动作
        face_registration_action = QAction("人脸注册", self)
        face_registration_action.triggered.connect(self.show_face_registration)
        algorithm_menu.addAction(face_registration_action)

        # 视频处理子菜单
        video_processing_menu = algorithm_menu.addMenu("视频处理")

        # 灰度效果
        gray_effect_action = QAction("灰度效果", self)
        gray_effect_action.setCheckable(True)
        gray_effect_action.triggered.connect(lambda checked: self.set_video_effect(
            "灰度" if checked else None))
        video_processing_menu.addAction(gray_effect_action)

        # 边缘检测效果
        edge_effect_action = QAction("边缘检测", self)
        edge_effect_action.setCheckable(True)
        edge_effect_action.triggered.connect(lambda checked: self.set_video_effect(
            "边缘检测" if checked else None))
        video_processing_menu.addAction(edge_effect_action)

        # 实时人脸检测
        realtime_face_detection_action = QAction("实时人脸检测", self)
        realtime_face_detection_action.setCheckable(True)
        realtime_face_detection_action.triggered.connect(
            self.toggle_realtime_face_detection)
        video_processing_menu.addAction(realtime_face_detection_action)

        # 帮助菜单
        help_menu = menu_bar.addMenu("帮助")

        # 使用说明动作
        help_action = QAction("使用说明", self)
        help_action.triggered.connect(self.show_help)
        help_menu.addAction(help_action)

        # 关于动作
        about_action = QAction("关于", self)
        about_action.triggered.connect(self.show_about)
        help_menu.addAction(about_action)

        # 保存菜单项引用，用于后续状态更新
        self.gray_effect_action = gray_effect_action
        self.edge_effect_action = edge_effect_action
        self.realtime_face_detection_action = realtime_face_detection_action

    def _create_tool_bar(self):
        """创建工具栏"""
        tool_bar = QToolBar("工具栏", self)
        self.addToolBar(tool_bar)

        # 加载图像按钮
        load_image_btn = QAction("加载图像", self)
        load_image_btn.setIcon(QIcon.fromTheme("document-open"))
        load_image_btn.triggered.connect(self.load_image)
        tool_bar.addAction(load_image_btn)

        # 加载视频按钮
        load_video_btn = QAction("加载视频", self)
        load_video_btn.setIcon(QIcon.fromTheme("media-playlist-open"))
        load_video_btn.triggered.connect(self.load_video)
        tool_bar.addAction(load_video_btn)

        # 使用摄像头按钮
        use_camera_btn = QAction("使用摄像头", self)
        use_camera_btn.setIcon(QIcon.fromTheme("camera-photo"))
        use_camera_btn.triggered.connect(self.use_camera)
        tool_bar.addAction(use_camera_btn)

        tool_bar.addSeparator()

        # 保存结果按钮
        save_result_btn = QAction("保存结果", self)
        save_result_btn.setIcon(QIcon.fromTheme("document-save"))
        save_result_btn.triggered.connect(self.save_result)
        tool_bar.addAction(save_result_btn)

        tool_bar.addSeparator()

        # 播放/暂停按钮（视频模式）
        self.play_pause_btn = QAction("播放", self)
        self.play_pause_btn.setIcon(QIcon.fromTheme("media-playback-start"))
        self.play_pause_btn.triggered.connect(self.toggle_play_pause)
        self.play_pause_btn.setEnabled(False)
        tool_bar.addAction(self.play_pause_btn)

        # 停止按钮（视频模式）
        self.stop_btn = QAction("停止", self)
        self.stop_btn.setIcon(QIcon.fromTheme("media-playback-stop"))
        self.stop_btn.triggered.connect(self.stop_video)
        self.stop_btn.setEnabled(False)
        tool_bar.addAction(self.stop_btn)

    def _create_main_layout(self):
        """创建主布局"""
        # 创建中央部件
        central_widget = QWidget()
        self.setCentralWidget(central_widget)

        # 主布局
        main_layout = QHBoxLayout(central_widget)

        # 创建图像显示区域
        self.canvas = CVCanvas()
        self.canvas.show_crop_rect = False
        self.canvas.mouse_pressed.connect(self.on_canvas_mouse_pressed)
        self.canvas.mouse_released.connect(self.on_canvas_mouse_released)
        self.canvas.mouse_moved.connect(self.on_canvas_mouse_moved)

        main_layout.addWidget(self.canvas)

    def _create_dock_widgets(self):
        """创建停靠窗口"""
        # 参数设置面板
        param_dock = QDockWidget("参数设置", self)
        param_dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)

        # 创建参数设置面板内容
        param_widget = QWidget()
        param_layout = QVBoxLayout(param_widget)

        # 亮度/对比度调整组
        brightness_contrast_group = QGroupBox("亮度/对比度调整")
        brightness_contrast_layout = QGridLayout(brightness_contrast_group)

        # 亮度滑块
        brightness_label = QLabel("亮度:")
        self.brightness_slider = QSlider(Qt.Horizontal)
        self.brightness_slider.setRange(-100, 100)
        self.brightness_slider.setValue(0)
        self.brightness_slider.valueChanged.connect(self.update_brightness_contrast)

        self.brightness_value = QLabel("0")
        self.brightness_slider.valueChanged.connect(lambda value: self.brightness_value.setText(str(value)))

        # 对比度滑块
        contrast_label = QLabel("对比度:")
        self.contrast_slider = QSlider(Qt.Horizontal)
        self.contrast_slider.setRange(1, 30)
        self.contrast_slider.setValue(10)  # 对应1.0
        self.contrast_slider.valueChanged.connect(self.update_brightness_contrast)

        self.contrast_value = QLabel("1.0")
        self.contrast_slider.valueChanged.connect(
            lambda value: self.contrast_value.setText(f"{value / 10:.1f}"))

        # 添加到布局
        brightness_contrast_layout.addWidget(brightness_label, 0, 0)
        brightness_contrast_layout.addWidget(self.brightness_slider, 0, 1)
        brightness_contrast_layout.addWidget(self.brightness_value, 0, 2)

        brightness_contrast_layout.addWidget(contrast_label, 1, 0)
        brightness_contrast_layout.addWidget(self.contrast_slider, 1, 1)
        brightness_contrast_layout.addWidget(self.contrast_value, 1, 2)

        # 饱和度调整组
        saturation_group = QGroupBox("饱和度调整")
        saturation_layout = QGridLayout(saturation_group)

        # 饱和度滑块
        saturation_label = QLabel("饱和度:")
        self.saturation_slider = QSlider(Qt.Horizontal)
        self.saturation_slider.setRange(0, 30)
        self.saturation_slider.setValue(10)  # 对应1.0
        self.saturation_slider.valueChanged.connect(self.update_saturation)

        self.saturation_value = QLabel("1.0")
        self.saturation_slider.valueChanged.connect(
            lambda value: self.saturation_value.setText(f"{value / 10:.1f}"))

        # 添加到布局
        saturation_layout.addWidget(saturation_label, 0, 0)
        saturation_layout.addWidget(self.saturation_slider, 0, 1)
        saturation_layout.addWidget(self.saturation_value, 0, 2)

        # 平滑处理组
        smoothing_group = QGroupBox("平滑处理")
        smoothing_layout = QGridLayout(smoothing_group)

        # 平滑方法选择
        smoothing_label = QLabel("方法:")
        self.smoothing_combo = QComboBox()
        self.smoothing_combo.addItems(["均值滤波", "高斯滤波", "中值滤波"])

        # 核大小选择
        kernel_size_label = QLabel("核大小:")
        self.kernel_size_spin = QSpinBox()
        self.kernel_size_spin.setRange(1, 15)
        self.kernel_size_spin.setValue(3)
        self.kernel_size_spin.setSingleStep(2)  # 只能是奇数

        # 应用按钮
        apply_smoothing_btn = QPushButton("应用")
        apply_smoothing_btn.clicked.connect(self.apply_smoothing)

        # 添加到布局
        smoothing_layout.addWidget(smoothing_label, 0, 0)
        smoothing_layout.addWidget(self.smoothing_combo, 0, 1)

        smoothing_layout.addWidget(kernel_size_label, 1, 0)
        smoothing_layout.addWidget(self.kernel_size_spin, 1, 1)

        smoothing_layout.addWidget(apply_smoothing_btn, 2, 0, 1, 2)

        # 添加所有组到参数布局
        param_layout.addWidget(brightness_contrast_group)
        param_layout.addWidget(saturation_group)
        param_layout.addWidget(smoothing_group)

        # 设置布局间距
        param_layout.addStretch()

        # 设置参数面板内容
        param_dock.setWidget(param_widget)
        self.addDockWidget(Qt.LeftDockWidgetArea, param_dock)

        # 结果显示面板
        result_dock = QDockWidget("结果显示", self)
        result_dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)

        # 创建结果显示面板内容
        result_widget = QWidget()
        result_layout = QVBoxLayout(result_widget)

        # 结果列表
        self.result_list = QListWidget()

        # 添加到布局
        result_layout.addWidget(self.result_list)

        # 设置结果面板内容
        result_dock.setWidget(result_widget)
        self.addDockWidget(Qt.RightDockWidgetArea, result_dock)

    def _create_status_bar(self):
        """创建状态栏"""
        status_bar = QStatusBar()
        self.setStatusBar(status_bar)

        # 添加永久部件显示坐标
        self.coord_label = QLabel("坐标: (0, 0)")
        status_bar.addPermanentWidget(self.coord_label)

        # 添加永久部件显示尺寸
        self.size_label = QLabel("尺寸: 0x0")
        status_bar.addPermanentWidget(self.size_label)

    def _connect_signals(self):
        """连接信号和槽"""
        # 视频处理器信号
        if self.video_processor:
            self.video_processor.frame_updated.connect(self.update_video_frame)
            self.video_processor.processing_finished.connect(self.video_processing_finished)
            self.video_processor.detection_updated.connect(self.update_face_detection_results)

    def load_image(self):
        """加载图像"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择图像", "", "图像文件 (*.png *.jpg *.jpeg *.bmp *.gif)")

        if file_path:
            # 停止当前视频处理
            self.stop_video()

            # 更新模式
            self.mode = "image"

            # 禁用视频相关UI
            self.play_pause_btn.setEnabled(False)
            self.stop_btn.setEnabled(False)
            self.gray_effect_action.setEnabled(False)
            self.edge_effect_action.setEnabled(False)
            self.realtime_face_detection_action.setEnabled(False)

            # 重置UI状态
            self.gray_effect_action.setChecked(False)
            self.edge_effect_action.setChecked(False)
            self.realtime_face_detection_action.setChecked(False)

            # 加载图像
            if self.processor.load_image(file_path):
                # 更新显示
                self.update_image_display()

                # 更新状态栏
                h, w = self.processor.image.shape[:2]
                self.size_label.setText(f"尺寸: {w}x{h}")

                # 更新状态信息
                self.statusBar().showMessage(f"已加载图像: {os.path.basename(file_path)}")
            else:
                QMessageBox.warning(self, "加载失败", "无法加载所选图像")

    def load_video(self):
        """加载视频"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择视频", "", "视频文件 (*.mp4 *.avi *.mov *.mkv)")

        if file_path:
            # 停止当前视频处理
            self.stop_video()

            # 更新模式
            self.mode = "video"

            # 启用视频相关UI
            self.play_pause_btn.setEnabled(True)
            self.play_pause_btn.setText("播放")
            self.play_pause_btn.setIcon(QIcon.fromTheme("media-playback-start"))

            self.stop_btn.setEnabled(True)
            self.gray_effect_action.setEnabled(True)
            self.edge_effect_action.setEnabled(True)
            self.realtime_face_detection_action.setEnabled(True)

            # 初始化视频处理器
            self.video_processor = VideoProcessor(file_path, self.processor)
            self._connect_signals()

            # 更新状态信息
            self.statusBar().showMessage(f"已加载视频: {os.path.basename(file_path)}")

    def use_camera(self):
        """使用摄像头"""
        # 停止当前视频处理
        self.stop_video()

        # 更新模式
        self.mode = "video"

        # 启用视频相关UI
        self.play_pause_btn.setEnabled(True)
        self.play_pause_btn.setText("播放")
        self.play_pause_btn.setIcon(QIcon.fromTheme("media-playback-start"))

        self.stop_btn.setEnabled(True)
        self.gray_effect_action.setEnabled(True)
        self.edge_effect_action.setEnabled(True)
        self.realtime_face_detection_action.setEnabled(True)

        # 初始化视频处理器（使用默认摄像头）
        self.video_processor = VideoProcessor(0, self.processor)
        self._connect_signals()

        # 开始播放
        self.video_processor.start()
        self.play_pause_btn.setText("暂停")
        self.play_pause_btn.setIcon(QIcon.fromTheme("media-playback-pause"))

        # 更新状态信息
        self.statusBar().showMessage("已连接摄像头")

    def save_result(self):
        """保存结果"""
        if self.mode == "image" and self.processor.image is not None:
            file_path, _ = QFileDialog.getSaveFileName(
                self, "保存图像", "", "图像文件 (*.png *.jpg *.jpeg)")

            if file_path:
                try:
                    cv2.imwrite(file_path, self.processor.image)
                    self.statusBar().showMessage(f"已保存图像: {os.path.basename(file_path)}")
                except Exception as e:
                    QMessageBox.warning(self, "保存失败", f"无法保存图像: {str(e)}")
        elif self.mode == "video" and self.canvas.pixmap:
            file_path, _ = QFileDialog.getSaveFileName(
                self, "保存截图", "", "图像文件 (*.png *.jpg *.jpeg)")

            if file_path:
                try:
                    # 将当前显示的视频帧保存为图像
                    self.canvas.pixmap.save(file_path)
                    self.statusBar().showMessage(f"已保存截图: {os.path.basename(file_path)}")
                except Exception as e:
                    QMessageBox.warning(self, "保存失败", f"无法保存截图: {str(e)}")
        else:
            QMessageBox.warning(self, "保存失败", "没有可保存的内容")

    def undo(self):
        """撤销操作"""
        if self.processor.original_image is not None:
            self.processor.image = self.processor.original_image.copy()
            self.update_image_display()
            self.statusBar().showMessage("已撤销操作")

    def redo(self):
        """重做操作"""
        # 目前没有实现历史记录，因此无法重做
        self.statusBar().showMessage("重做功能尚未实现")

    def rotate_image(self, angle):
        """旋转图像"""
        if self.processor.image is not None:
            if self.processor.rotate_image(angle):
                self.update_image_display()
                self.statusBar().showMessage(f"已旋转图像: {angle}度")
            else:
                self.statusBar().showMessage("旋转图像失败")

    def rotate_image_free(self):
        """自定义角度旋转图像"""
        if self.processor.image is not None:
            # 创建对话框获取角度
            angle, ok = QDialog.getInt(
                self, "旋转图像", "输入旋转角度:", 0, -359, 359, 1)

            if ok:
                if self.processor.rotate_image(angle):
                    self.update_image_display()
                    self.statusBar().showMessage(f"已旋转图像: {angle}度")
                else:
                    self.statusBar().showMessage("旋转图像失败")

    def resize_image(self, percent):
        """缩放图像"""
        if self.processor.image is not None:
            if self.processor.resize_image(percent):
                self.update_image_display()
                self.statusBar().showMessage(f"已缩放图像: {percent}%")
            else:
                self.statusBar().showMessage("缩放图像失败")

    def resize_image_free(self):
        """自定义比例缩放图像"""
        if self.processor.image is not None:
            # 创建对话框获取比例
            percent, ok = QDialog.getInt(
                self, "缩放图像", "输入缩放百分比:", 100, 1, 1000, 1)

            if ok:
                if self.processor.resize_image(percent):
                    self.update_image_display()
                    self.statusBar().showMessage(f"已缩放图像: {percent}%")
                else:
                    self.statusBar().showMessage("缩放图像失败")

    def toggle_crop_mode(self):
        """切换裁剪模式"""
        if self.processor.image is not None:
            self.canvas.show_crop_rect = not self.canvas.show_crop_rect

            if self.canvas.show_crop_rect:
                self.statusBar().showMessage("裁剪模式已启用，请在图像上绘制裁剪区域")
            else:
                self.statusBar().showMessage("裁剪模式已禁用")

    def on_canvas_mouse_pressed(self, pos):
        """画布鼠标按下事件处理"""
        if self.canvas.show_crop_rect:
            self.statusBar().showMessage(f"开始裁剪区域: ({pos.x()}, {pos.y()})")

    def on_canvas_mouse_released(self, pos):
        """画布鼠标释放事件处理"""
        if self.canvas.show_crop_rect:
            # 获取裁剪矩形
            rect = self.canvas.get_crop_rect()

            if rect:
                x1, y1, x2, y2 = rect

                # 确保矩形有效
                if x2 > x1 and y2 > y1:
                    # 执行裁剪
                    if self.processor.crop_image(x1, y1, x2, y2):
                        # 禁用裁剪模式
                        self.canvas.show_crop_rect = False

                        # 更新显示
                        self.update_image_display()

                        # 更新状态栏
                        h, w = self.processor.image.shape[:2]
                        self.size_label.setText(f"尺寸: {w}x{h}")

                        self.statusBar().showMessage(f"已裁剪图像: {w}x{h}")
                    else:
                        self.statusBar().showMessage("裁剪图像失败")
                else:
                    self.statusBar().showMessage("无效的裁剪区域")
            else:
                self.statusBar().showMessage("未定义裁剪区域")

    def on_canvas_mouse_moved(self, pos):
        """画布鼠标移动事件处理"""
        self.coord_label.setText(f"坐标: ({pos.x()}, {pos.y()})")

    def update_brightness_contrast(self):
        """更新亮度和对比度"""
        if self.processor.image is not None:
            brightness = self.brightness_slider.value()
            contrast = self.contrast_slider.value() / 10.0

            # 复制原始图像
            self.processor.image = self.processor.original_image.copy()

            # 应用亮度和对比度调整
            if self.processor.adjust_brightness_contrast(brightness, contrast):
                self.update_image_display()
                self.statusBar().showMessage(f"已调整亮度: {brightness}, 对比度: {contrast:.1f}")
            else:
                self.statusBar().showMessage("调整亮度/对比度失败")

    def update_saturation(self):
        """更新饱和度"""
        if self.processor.image is not None:
            saturation = self.saturation_slider.value() / 10.0

            # 复制原始图像
            self.processor.image = self.processor.original_image.copy()

            # 应用亮度和对比度调整
            if self.processor.adjust_saturation(saturation):
                self.update_image_display()
                self.statusBar().showMessage(f"已调整饱和度: {saturation:.1f}")
            else:
                self.statusBar().showMessage("调整饱和度失败")

    def apply_smoothing(self):
        """应用平滑处理"""
        if self.processor.image is not None:
            method = self.smoothing_combo.currentText()
            kernel_size = self.kernel_size_spin.value()

            # 复制原始图像
            self.processor.image = self.processor.original_image.copy()

            # 应用平滑处理
            if self.processor.apply_smoothing(method, kernel_size):
                self.update_image_display()
                self.statusBar().showMessage(f"已应用{method}, 核大小: {kernel_size}")
            else:
                self.statusBar().showMessage(f"应用{method}失败")

    def detect_shapes(self):
        """检测形状"""
        if self.processor.image is not None:
            # 复制原始图像
            self.processor.image = self.processor.original_image.copy()

            # 显示进度
            progress_dialog = QProgressDialog("正在检测形状...", "取消", 0, 0, self)
            progress_dialog.setWindowModality(Qt.WindowModal)
            progress_dialog.show()

            # 执行形状检测
            shapes = self.processor.detect_shapes()

            # 关闭进度对话框
            progress_dialog.close()

            if shapes:
                # 更新显示
                self.update_image_display()

                # 更新结果列表
                self.result_list.clear()
                self.result_list.addItem(f"检测到 {len(shapes)} 个形状:")

                for i, shape in enumerate(shapes):
                    self.result_list.addItem(
                        f"{i + 1}. {shape['type']} (面积: {shape['area']:.1f}像素)")

                self.statusBar().showMessage(f"已检测到 {len(shapes)} 个形状")
            else:
                self.result_list.clear()
                self.result_list.addItem("未检测到形状")
                self.statusBar().showMessage("未检测到形状")

    def detect_faces(self):
        """检测人脸"""
        if self.processor.image is not None:
            # 复制原始图像
            self.processor.image = self.processor.original_image.copy()

            # 显示进度
            progress_dialog = QProgressDialog("正在检测人脸...", "取消", 0, 0, self)
            progress_dialog.setWindowModality(Qt.WindowModal)
            progress_dialog.show()

            # 执行人脸检测
            faces = self.processor.detect_faces()

            # 关闭进度对话框
            progress_dialog.close()

            if faces:
                # 更新显示
                self.update_image_display()

                # 更新结果列表
                self.result_list.clear()
                self.result_list.addItem(f"检测到 {len(faces)} 个人脸:")

                for i, face in enumerate(faces):
                    top, right, bottom, left = face["location"]
                    self.result_list.addItem(
                        f"{i + 1}. 人脸位置: 上={top}, 右={right}, 下={bottom}, 左={left}")

                self.statusBar().showMessage(f"已检测到 {len(faces)} 个人脸")
            else:
                self.result_list.clear()
                self.result_list.addItem("未检测到人脸")
                self.statusBar().showMessage("未检测到人脸")

    def recognize_faces(self):
        """人脸识别"""
        if self.processor.image is not None:
            # 复制原始图像
            self.processor.image = self.processor.original_image.copy()

            # 显示进度
            progress_dialog = QProgressDialog("正在识别人脸...", "取消", 0, 0, self)
            progress_dialog.setWindowModality(Qt.WindowModal)
            progress_dialog.show()

            # 执行人脸检测
            faces = self.processor.detect_faces()

            if faces:
                # 执行人脸识别
                face_encodings = [face["encoding"] for face in faces]
                results = self.processor.recognize_faces(face_encodings)

                # 在图像上显示识别结果
                output = self.processor.image.copy()

                for i, (face, result) in enumerate(zip(faces, results)):
                    top, right, bottom, left = face["location"]

                    # 在人脸框上方显示识别结果
                    cv2.rectangle(output, (left, top), (right, bottom), (0, 0, 255), 2)
                    cv2.putText(output, f"{result['name']} ({result['similarity']}%)",
                                (left, top - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9,
                                (0, 0, 255), 2)

                self.processor.image = output

                # 更新显示
                self.update_image_display()

                # 更新结果列表
                self.result_list.clear()
                self.result_list.addItem(f"识别出 {len(results)} 个人脸:")

                for i, result in enumerate(results):
                    self.result_list.addItem(
                        f"{i + 1}. {result['name']} (相似度: {result['similarity']}%)")

                self.statusBar().showMessage(f"已识别 {len(results)} 个人脸")
            else:
                self.result_list.clear()
                self.result_list.addItem("未检测到人脸")
                self.statusBar().showMessage("未检测到人脸")

            # 关闭进度对话框
            progress_dialog.close()

    def show_face_registration(self):
        """显示人脸注册对话框"""
        self.face_registration_dialog.show()

    def register_face(self, name, encoding):
        """注册人脸"""
        if self.processor.save_face_encoding(name, encoding):
            self.statusBar().showMessage(f"已成功注册人脸: {name}")
            QMessageBox.information(self, "成功", f"已成功注册人脸: {name}")
        else:
            self.statusBar().showMessage(f"注册人脸失败: {name}")
            QMessageBox.warning(self, "失败", f"注册人脸失败: {name}")

    def toggle_play_pause(self):
        """切换播放/暂停状态"""
        if self.video_processor:
            if self.video_processor.running:
                # 暂停
                self.video_processor.running = False
                self.play_pause_btn.setText("播放")
                self.play_pause_btn.setIcon(QIcon.fromTheme("media-playback-start"))
                self.statusBar().showMessage("视频已暂停")
            else:
                # 播放
                self.video_processor.running = True
                self.video_processor.start()
                self.play_pause_btn.setText("暂停")
                self.play_pause_btn.setIcon(QIcon.fromTheme("media-playback-pause"))
                self.statusBar().showMessage("视频正在播放")

    def stop_video(self):
        """停止视频处理"""
        if self.video_processor:
            self.video_processor.running = False
            self.video_processor.wait()
            self.play_pause_btn.setText("播放")
            self.play_pause_btn.setIcon(QIcon.fromTheme("media-playback-start"))
            self.statusBar().showMessage("视频已停止")

    def set_video_effect(self, effect_type):
        """设置视频效果"""
        if self.video_processor:
            self.video_processor.apply_effect = effect_type is not None
            self.video_processor.effect_type = effect_type

            # 确保只有一个效果被选中
            if effect_type == "灰度":
                self.edge_effect_action.setChecked(False)
            elif effect_type == "边缘检测":
                self.gray_effect_action.setChecked(False)

    def toggle_realtime_face_detection(self, enabled):
        """切换实时人脸检测"""
        if self.video_processor:
            self.video_processor.detect_faces = enabled

    def update_video_frame(self, frame):
        """更新视频帧显示"""
        self.canvas.set_image(frame)

    def video_processing_finished(self):
        """视频处理完成"""
        self.statusBar().showMessage("视频处理已完成")

    def update_face_detection_results(self, face_locations):
        """更新人脸检测结果"""
        self.result_list.clear()
        if face_locations:
            self.result_list.addItem(f"检测到 {len(face_locations)} 个人脸:")
            for i, (top, right, bottom, left) in enumerate(face_locations):
                self.result_list.addItem(
                    f"{i + 1}. 人脸位置: 上={top}, 右={right}, 下={bottom}, 左={left}")
        else:
            self.result_list.addItem("未检测到人脸")

    def update_image_display(self):
        """更新图像显示"""
        if self.processor.image is not None:
            self.canvas.set_image(self.processor.image)

    def show_help(self):
        """显示帮助信息"""
        QMessageBox.information(self, "使用说明", """
计算机视觉项目实施平台使用说明：

1. 文件操作：
   - 加载图像：从本地选择图像文件
   - 加载视频：从本地选择视频文件
   - 使用摄像头：连接并使用计算机摄像头

2. 图像处理：
   - 旋转：可将图像旋转90°、180°、270°或自定义角度
   - 缩放：可将图像按比例缩放
   - 裁剪：在图像上绘制区域进行裁剪

3. 色彩调整：
   - 亮度/对比度：通过滑块调整图像亮度和对比度
   - 饱和度：通过滑块调整图像饱和度

4. 平滑处理：
   - 可应用均值滤波、高斯滤波或中值滤波

5. 计算机视觉算法：
   - 形状识别：自动检测图像中的几何形状
   - 人脸检测：检测图像中的人脸
   - 人脸识别：识别图像中的已知人脸
   - 人脸注册：添加新的人脸到识别库

6. 视频处理：
   - 播放/暂停/停止：控制视频播放
   - 视频效果：可应用灰度、边缘检测等效果
   - 实时人脸检测：在视频中实时检测人脸
""")

    def show_about(self):
        """显示关于信息"""
        QMessageBox.about(self, "关于", """
计算机视觉项目实施平台 v1.0

这是一个基于Python和OpenCV的计算机视觉项目实施平台，
支持图像处理、计算机视觉算法和视频处理等功能。

主要功能：
- 图像处理：加载、显示、编辑、色彩调整等
- 计算机视觉算法：形状识别、人脸检测与识别
- 视频处理：视频播放、实时效果处理

技术栈：
- Python 3.9
- PyQt5
- OpenCV
- face_recognition
- NumPy
""")


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())